﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lichtspiel.Math;
using OpenTK.Graphics.OpenGL;

namespace Lichtspiel.Display
{
    public class PrimitivesDrawer
    {
        private const int CircleStep = 6;
        // TODO ist es sinnvoll andauernd enable smooth und disable smooth aufrufen oder nur einmal
        #region points
        public void DrawPoints(List<Vector2> vertices, Color color, float size) {
            DrawPoints(vertices, color, size, BlendMode.Alpha);
        }

        public void DrawPoints(List<Vector2> vertices, Color color, float size, BlendMode blendMode) {
            GL.PointSize(size);
            GL.Enable(EnableCap.PointSmooth);

            DrawVertices(PrimitiveMode.Points, vertices, color, blendMode);

            GL.Disable(EnableCap.PointSmooth);
        }
        #endregion

        #region lines lineloop linestrip
        public void DrawLines(List<Vector2> vertices, Color color, float width) {
            DrawLines(vertices, color, width, BlendMode.Alpha);
        }

        public void DrawLines(List<Vector2> vertices, Color color, float width, BlendMode blendMode) {
            GL.LineWidth(width);
            GL.Enable(EnableCap.LineSmooth);

            DrawVertices(PrimitiveMode.Lines, vertices, color, blendMode);

            GL.Disable(EnableCap.LineSmooth);
        }

        public void DrawLineLoop(List<Vector2> vertices, Color color, float width) {
            DrawLineLoop(vertices, color, width, BlendMode.Alpha);
        }

        public void DrawLineLoop(List<Vector2> vertices, Color color, float width, BlendMode blendMode) {
            GL.LineWidth(width);
            GL.Enable(EnableCap.LineSmooth);

            DrawVertices(PrimitiveMode.LineLoop, vertices, color, blendMode);

            GL.Disable(EnableCap.LineSmooth);
        }

        public void DrawLineStrip(List<Vector2> vertices, Color color, float width) {
            DrawLineStrip(vertices, color, width, BlendMode.Alpha);
        }

        public void DrawLineStrip(List<Vector2> vertices, Color color, float width, BlendMode blendMode) {
            GL.LineWidth(width);
            GL.Enable(EnableCap.LineSmooth);

            DrawVertices(PrimitiveMode.LineStrip, vertices, color, blendMode);

            GL.Disable(EnableCap.LineSmooth);
        }
        #endregion

        #region rectangles
        public void DrawRectangle(Rectangle rect, Color color) {
            DrawRectangle(rect, color, BlendMode.Alpha);
        }

        public void DrawRectangle(Rectangle rect, Color color, BlendMode blendMode) {
            List<Vector2> vertices = new List<Vector2>
                                         {
                                             new Vector2(rect.X, rect.Y),
                                             new Vector2(rect.X + rect.Width, rect.Y),
                                             new Vector2(rect.X + rect.Width, rect.Y + rect.Height),
                                             new Vector2(rect.X, rect.Y + rect.Height)
                                         };

            DrawVertices(PrimitiveMode.Quads, vertices, color, blendMode);
        }

        public void DrawRectangleOutline(Rectangle rect, Color color, float outlineWidth) {
            DrawRectangleOutline(rect, color, outlineWidth, BlendMode.Alpha);
        }

        public void DrawRectangleOutline(Rectangle rect, Color color, float outlineWidth, BlendMode blendMode) {
            List<Vector2> vertices = new List<Vector2>
                                         {
                                             new Vector2(rect.X, rect.Y),
                                             new Vector2(rect.X + rect.Width, rect.Y),
                                             new Vector2(rect.X + rect.Width, rect.Y + rect.Height),
                                             new Vector2(rect.X, rect.Y + rect.Height)
                                         };

            DrawLineLoop(vertices, color, outlineWidth, blendMode);
        }

        public void DrawRectangle(Rectangle rect, ColorGradient gradient) {
            List<ColoredVertex> vertices = new List<ColoredVertex>
                                               {
                                                   new ColoredVertex(new Vector2(rect.X, rect.Y), gradient.TopLeft),
                                                   new ColoredVertex(new Vector2(rect.X + rect.Width, rect.Y),gradient.TopRight),
                                                   new ColoredVertex(new Vector2(rect.X + rect.Width, rect.Y + rect.Height),gradient.BottomRight),
                                                   new ColoredVertex(new Vector2(rect.X, rect.Y + rect.Height),gradient.BottomLeft)
                                               };

            DrawVertices(PrimitiveMode.Quads, vertices);
        }
        #endregion

        #region polygon
        /// <summary>
        /// Convex Polygon
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="color"></param>
        public void DrawPolygon(List<Vector2> vertices, Color color) {
            DrawPolygon(vertices, color, BlendMode.Alpha);
        }


        /// <summary>
        /// Convex Polygon
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="color"></param>
        /// <param name="blendMode"></param>
        public void DrawPolygon(List<Vector2> vertices, Color color, BlendMode blendMode) {
            DrawVertices(PrimitiveMode.Polygon, vertices, color, blendMode);
        }

        /// <summary>
        /// Convex Polygon
        /// </summary>
        /// <param name="vertices"></param>
        public void DrawPolygon(List<ColoredVertex> vertices) {
            DrawVertices(PrimitiveMode.Polygon, vertices);
        }
        #endregion

        #region circle
        public void DrawCircle(Vector2 center, Color color, float radius) {
            DrawCircle(center, color, radius, BlendMode.Alpha);
        }

        public void DrawCircle(Vector2 center, Color color, float radius, BlendMode blendMode) {
            List<Vector2> vertices = new List<Vector2>(360 / CircleStep + 1);
            vertices.Add(center);
            for (int angle = 0; angle < 360; angle += CircleStep) {
                float radians = MathHelper.ToRadians(angle);
                vertices.Add(new Vector2(center.X + System.Math.Cos(radians) * radius, center.Y + System.Math.Sin(radians) * radius));
            }
            float lastAngle = MathHelper.ToRadians(360);
            vertices.Add(new Vector2(center.X + System.Math.Cos(lastAngle) * radius, center.Y + System.Math.Sin(lastAngle) * radius));

            DrawVertices(PrimitiveMode.TriangleFan, vertices, color, blendMode);
        }
        public void DrawCircleOutline(Vector2 center, Color color, float radius, float lineWidth) {
            DrawCircleOutline(center, color, radius, lineWidth, BlendMode.Alpha);
        }

        public void DrawCircleOutline(Vector2 center, Color color, float radius, float lineWidth, BlendMode blendMode) {
            List<Vector2> vertices = new List<Vector2>(360 / CircleStep);
            for (int angle = 0; angle < 360; angle += CircleStep) {
                float rad = MathHelper.ToRadians(angle);
                vertices.Add(new Vector2(center.X + System.Math.Cos(rad) * radius, center.Y + System.Math.Sin(rad) * radius));
            }

            DrawLineLoop(vertices, color, lineWidth, blendMode);
        }

        public void DrawCircle(Vector2 center, Color innerColor, Color outerColor, float radius) {
            List<ColoredVertex> vertices = new List<ColoredVertex>(360 / CircleStep + 1);
            vertices.Add(new ColoredVertex(center, innerColor));
            for (int angle = 0; angle < 360; angle += CircleStep) {
                float rad = MathHelper.ToRadians(angle);
                vertices.Add(new ColoredVertex(new Vector2(center.X + System.Math.Cos(rad) * radius, center.Y + System.Math.Sin(rad) * radius), outerColor));
            }

            float lastAngle = MathHelper.ToRadians(360);
            vertices.Add(new ColoredVertex(new Vector2(center.X + System.Math.Cos(lastAngle) * radius, center.Y + System.Math.Sin(lastAngle) * radius), outerColor));

            DrawVertices(PrimitiveMode.TriangleFan, vertices);
        }
        #endregion

        #region vertices
        public void DrawVertices(PrimitiveMode mode, List<Vector2> vertices, Color color, BlendMode blendMode) {
            bool blendEnabled = false;

            GL.Disable(EnableCap.Texture2D);

            GL.Color4(color.R, color.G, color.B, color.A);
            if (blendMode != BlendMode.None) {
                if ((blendMode == BlendMode.Alpha && color.A < 1) || blendMode != BlendMode.Alpha) {
                    GLHelper.EnableBlend(blendMode, color);
                    blendEnabled = true;
                }
            }

            GL.Begin((BeginMode) mode);
            foreach (Vector2 v in vertices) {
                GL.Vertex3(v.X, v.Y, 0);
            }
            GL.End();

            if (blendEnabled)
                GLHelper.DisableBlend();

            GL.Enable(EnableCap.Texture2D);
        }

        public void DrawVertices(PrimitiveMode mode, List<ColoredVertex> vertices) {
            GL.Disable(EnableCap.Texture2D);

            GLHelper.EnableBlend(BlendMode.Alpha, Color.White);

            GL.Begin((BeginMode)mode);
            int i = 0;
            foreach (ColoredVertex v in vertices) {
                GL.Color4(v.Color.R, v.Color.G, v.Color.B, v.Color.A);

                GL.Vertex3(v.Vector.X, v.Vector.Y, 0);
            }
            GL.End();

            GLHelper.DisableBlend();

            GL.Enable(EnableCap.Texture2D);
        }
        #endregion
    }
}
